import json
import time
import logging

import requests
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.db import transaction
from django_redis import get_redis_connection

from libs.auth import check_post_request
from libs.common import success, error
from libs.form import RawJsonForm
from libs.fund import create_member_fund_log_sn
from libs.goods import create_order_sn
from trade.models.member_goods import MemberGoods
from trade.models.goods import Goods
from trade.models.order import Orders
from trade.models.deal_order_log import DealOrderLogs
from account.models import Members, MemberFundLogs
from .member import get_member
from trade.settings import env
from trade.constants import TRADE_TYPE_SELL, ORDER_STATUS_CANCEL, SUBJECT_TYPE_THAW_FUNDS, SUBJECT_TYPE_BUY_DEAL

logger = logging.getLogger(__name__)


# 会员下单，买入指定商品
# 1. 查询商品数量、状态
# 2. 查询会员状态、余额
# 3. 写入订单，请求撮合接口
def buy(request):
    return_code, return_content = check_post_request(request)
    if return_code != 0:
        return return_content
    member_id = return_content
    form = RawJsonForm(request.body)
    goods_sn = form.get('goods_sn')
    if goods_sn is None:
        return error('商品编号不能为空')
    amount = form.get('amount')
    if amount is None or int(amount) <= 0:
        return error('数量不能为空')
    price = form.get('price')
    if price is None or int(price) <= 0:
        return error('商品价格不能为空')
    return exec_buy(member_id, goods_sn, amount, price)


# 会员下单，卖出指定商品
# 1. 查询会员持有商品数量、状态
# 2. 写入订单，请求撮合接口
def sell(request):
    return_code, return_content = check_post_request(request)
    if return_code != 0:
        return return_content
    member_id = return_content

    form = RawJsonForm(request.body)
    goods_sn = form.get('goods_sn')
    if goods_sn is None:
        return error('商品编号不能为空')
    amount = form.get('amount')
    if amount is None or int(amount) <= 0:
        return error('数量不能为空')
    price = form.get('price')
    if price is None or int(price) <= 0:
        return error('商品价格不能为空')
    return exec_sell(member_id, goods_sn, amount, price)


def exec_buy(member_id, goods_sn, amount, price):
    total_price = int(price) * int(amount)

    fields = ["status"]
    try:
        goods = Goods.objects.values(*fields).get(sn=goods_sn)
    except MemberGoods.DoesNotExist:
        return error('商品不存在')
    if goods['status'] != 1:
        return error('商品已关闭交易')

    member, err = get_member(member_id, "amount", "frozen_funds", "ver")
    if err is not None:
        error(err)

    if member['amount'] < total_price:
        return error('会员余额不足')

    now = int(time.time())

    try:
        with transaction.atomic():
            sp = transaction.savepoint()

            order_sn = create_order_sn()
            order = Orders()
            order.member_id = member_id
            order.goods_sn = goods_sn
            order.order_sn = order_sn
            order.trade_type = 1
            order.price = price
            order.amount = amount
            order.total_price = total_price
            order.status = 0
            order.created = now
            order.updated = now
            order.save()

            fields = ['id', 'sn', 'amount', 'frozen_funds', 'ver']
            member_dict = Members.objects.values(*fields).get(id=member_id)

            amount_prev = member_dict['amount']
            ver = member_dict['ver']
            amount_next = amount_prev - total_price
            new_mem_data = {
                'amount': amount_next,
                'frozen_funds': member_dict['frozen_funds'] + total_price,
                'ver': ver + 1,
                'updated': int(time.time())
            }
            affected = Members.objects.filter(id=member_id, ver=ver).update(**new_mem_data)
            if affected == 0:
                raise Exception('更新用户资金失败')
            member_fund_logs = MemberFundLogs()
            member_fund_logs.sn = create_member_fund_log_sn()
            member_fund_logs.subject_type = SUBJECT_TYPE_BUY_DEAL
            member_fund_logs.subject_sn = order_sn
            member_fund_logs.trade_type = 1
            member_fund_logs.amount = amount
            member_fund_logs.member_id = member_id
            member_fund_logs.amount_prev = amount_prev
            member_fund_logs.amount_next = amount_next
            member_fund_logs.save()

            trade_data = {
                'order_sn': order_sn,
                'side': 1,
                'account_id': str(member_id),
                'symbol': goods_sn,
                'amount': str(amount),
                'price': str(price)
            }
            result, err = push_to_trade_engine(trade_data)
            if err is not None:
                raise Exception('调用撮合引擎异常')

    except Exception as ex:
        logger.error(ex)
        transaction.savepoint_rollback(sp)
        return error('买入失败，请重试')

    return success({'order_sn': order_sn})


def exec_sell(member_id, goods_sn, amount, price):
    total_price = int(price) * int(amount)
    fields = ['status']
    try:
        goods = Goods.objects.values(*fields).get(sn=goods_sn)
    except MemberGoods.DoesNotExist:
        return error('商品不存在')
    if goods['status'] != 1:
        return error('商品已关闭交易')

    fields = ['id', 'amount', 'frozen_amount', 'ver']
    try:
        member_goods = MemberGoods.objects.values(*fields).get(goods_sn=goods_sn, member_id=member_id)
    except MemberGoods.DoesNotExist:
        return error('商品不存在')
    ver = member_goods['ver']
    member_goods_id = member_goods['id']
    amount_next = member_goods['amount'] - amount
    if amount_next < 0:
        return error('商品数量不足')

    try:
        with transaction.atomic():
            mem_goods_next_data = {
                'amount': amount_next,
                'frozen_amount': member_goods['frozen_amount'] + amount,
                'ver': ver + 1,
                'updated': int(time.time())
            }
            affected = MemberGoods.objects.filter(id=member_goods_id, ver=ver).update(**mem_goods_next_data)

            if affected == 0:
                raise Exception('会员商品更新失败')

            order_sn = create_order_sn()
            now = int(time.time())
            order = Orders()
            order.member_id = member_id
            order.goods_sn = goods_sn
            order.order_sn = order_sn
            order.trade_type = 2
            order.price = price
            order.amount = amount
            order.total_price = total_price
            order.status = 0
            order.created = now
            order.updated = now
            order.save()

            trade_data = {
                'order_sn': order_sn,
                'side': 2,
                'account_id': str(member_id),
                'symbol': goods_sn,
                'amount': str(amount),
                'price': str(price)
            }
            result, err = push_to_trade_engine(trade_data)
            if err is not None:
                raise Exception('调用撮合引擎异常')

    except Exception as ex:
        logger.error(ex)
        return error('卖出失败，请重试')

    return success()


def push_to_trade_engine(trade_data):
    err = None
    result = None
    url = env('TRADE_ENGINE_URL')
    try:
        data = json.dumps(trade_data)
        headers = {'Content-Type': 'application/json'}
        resp = requests.post(url, headers=headers, data=data)
        result = json.loads(resp.text)
        if not ('code' in result and result['code'] == 0):
            raise Exception('接口返回错误')
    except Exception as ex:
        print('error', ex)
        err = '异常'
    return result, err


def get_member_goods(member_id, symbol):
    data = None
    try:
        member_goods_data = MemberGoods.objects.filter(
            member_id=member_id, goods_sn=symbol
        ).values('id', 'amount', 'ver').get()
    except MemberGoods.DoesNotExist:
        return data
    if 'id' in member_goods_data:
        data = member_goods_data
    return data


# 成交
def deal(request):
    try:
        with transaction.atomic():
            form = RawJsonForm(request.body)
            sn1 = form.get('sn1')
            sn2 = form.get('sn2')
            uid1 = form.get('uid1')
            uid2 = form.get('uid2')
            symbol = form.get('symbol')
            price = form.get('price')
            amount = form.get('amount')
            i_price = int(price)
            i_amount = int(amount)
            if not (i_price > 0 and i_amount > 0):
                return error()

            i_total_price = i_price * i_amount

            now = int(time.time())
            save_deal_order_log(uid1, sn1, price, amount, now)

            db_order_1 = Orders.objects.values(
                'id', 'price', 'amount', 'deal_amount', 'ver'
            ).filter(member_id=uid1, order_sn=sn1).get()

            deal_amount_1 = db_order_1['deal_amount'] + i_amount
            if deal_amount_1 == db_order_1['amount']:
                status_1 = 100
            elif deal_amount_1 < db_order_1['amount']:
                status_1 = 1
            else:
                return error()

            id_1 = db_order_1['id']
            ver_1 = db_order_1['ver']
            buy_order_dict = {
                'deal_amount': deal_amount_1,
                'status': status_1,
                'ver': ver_1 + 1
            }
            affected = Orders.objects.filter(
                id=id_1, ver=ver_1
            ).update(**buy_order_dict)
            if affected == 0:
                raise Exception('更新买家订单失败')

            # 买入，从冻结资金中，解冻成交金额，无资金变动
            buy_member_dict, err = get_member(uid1, 'amount', 'frozen_funds', 'ver')
            if err is not None:
                raise Exception('获取买家会员信息失败')

            i_total_wanna_price = db_order_1['price'] * i_amount  # 买家出价 * 成交数量
            surplus_frozen_funds = int(buy_member_dict['frozen_funds']) - i_total_wanna_price
            if surplus_frozen_funds < 0:
                raise Exception('剩余冻结资金小于0')

            buyer_funds_ver = int(buy_member_dict['ver'])
            buy_member_frozen_fund_dict = {
                'frozen_funds': surplus_frozen_funds,
                'ver': buyer_funds_ver + 1
            }

            i_back_money = i_total_wanna_price - i_total_price

            if i_back_money > 0:
                buy_member_frozen_fund_dict['amount'] = buy_member_dict['amount'] + i_back_money

            affected = Members.objects.filter(id=uid1, ver=buyer_funds_ver).update(**buy_member_frozen_fund_dict)
            if affected == 0:
                raise Exception('更新冻结资金失败')

            db_buyer_goods = get_member_goods(uid1, symbol)
            if db_buyer_goods is None:
                goods_dict = Goods.objects.filter(sn=symbol).values('title').get()
                buy_member_goods = MemberGoods()
                buy_member_goods.goods_sn = symbol
                buy_member_goods.member_id = uid1
                buy_member_goods.title = goods_dict['title']
                buy_member_goods.amount = i_amount
                buy_member_goods.frozen_amount = 0
                buy_member_goods.ver = 0
                buy_member_goods.created = now
                buy_member_goods.updated = now
                buy_member_goods.save()
            else:
                db_buyer_goods_ver = int(db_buyer_goods['ver'])
                buyer_goods_data = {
                    'amount': int(db_buyer_goods['amount']) + i_amount,
                    'ver': db_buyer_goods_ver + 1
                }
                affected = MemberGoods.objects.filter(
                    goods_sn=symbol, member_id=uid1, ver=db_buyer_goods_ver
                ).update(**buyer_goods_data)
                if affected == 0:
                    raise Exception('更新买家商品数量失败')

            # 卖出，从冻结商品中，解冻成交商品，记录资金变动
            sell_member_dict, err = get_member(uid2, 'amount', 'ver')
            if err is not None:
                raise Exception('获取卖家会员信息失败')

            save_deal_order_log(uid2, sn2, price, amount, now)

            db_order_2 = Orders.objects.values(
                'id', 'amount', 'deal_amount', 'ver'
            ).filter(member_id=uid2, order_sn=sn2).get()
            deal_amount_2 = db_order_2['deal_amount'] + i_amount
            if deal_amount_2 == db_order_2['amount']:
                status_2 = 100
            elif deal_amount_2 < db_order_2['amount']:
                status_2 = 1
            else:
                return error()
            id_2 = db_order_2['id']
            ver_2 = db_order_2['ver']
            sell_order_dict = {
                'deal_amount': deal_amount_2,
                'status': status_2,
                'ver': ver_2 + 1
            }
            affected = Orders.objects.filter(
                id=id_2, ver=ver_2
            ).update(**sell_order_dict)
            if affected == 0:
                return error()
            seller_goods_db_data = MemberGoods.objects.filter(
                member_id=uid2, goods_sn=symbol, deleted=0
            ).values('id', 'frozen_amount', 'ver').get()

            surplus_goods_amount = int(seller_goods_db_data['frozen_amount']) - i_amount
            if surplus_goods_amount < 0:
                raise Exception('剩余冻结商品小于0')
            member_goods_ver = int(seller_goods_db_data['ver'])
            seller_frozen_dict = {
                'frozen_amount': surplus_goods_amount,
                'ver': member_goods_ver + 1
            }
            affected = MemberGoods.objects.filter(
                id=seller_goods_db_data['id'], ver=member_goods_ver
            ).update(**seller_frozen_dict)
            if affected == 0:
                raise Exception('更新卖家冻结商品失败')

            amount_prev = sell_member_dict['amount']
            amount_next = amount_prev + i_total_price
            member_fund_logs = MemberFundLogs()
            member_fund_logs.sn = sn2
            member_fund_logs.subject_type = create_member_fund_log_sn()
            member_fund_logs.subject_sn = sn2
            member_fund_logs.member_id = uid2
            member_fund_logs.trade_type = TRADE_TYPE_SELL
            member_fund_logs.amount = i_total_price
            member_fund_logs.amount_prev = amount_prev
            member_fund_logs.amount_next = amount_next
            member_fund_logs.status = 1
            member_fund_logs.created = int(time.time())
            seller_funds_ver = int(sell_member_dict['ver'])
            seller_funds_data = {
                'amount': amount_next,
                'ver': seller_funds_ver + 1
            }
            affected = Members.objects.filter(
                id=uid2, ver=seller_funds_ver
            ).update(**seller_funds_data)

            update_goods_price(symbol, i_price)

            if affected == 0:
                raise Exception('更新卖家资金失败')

    except Exception as ex:
        print('######', ex)
        return error()

    socket_data = {
        'type': 'goods.price',
        'data': {
            'symbol': symbol,
            'price': price
        }
    }
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)('market', socket_data)

    return success()


# 获取价格缓存key
def get_goods_price_key(goods_sn):
    return 'p-{goods_sn}'.format(goods_sn=goods_sn)


def get_goods_price(goods_sn):
    price = None
    if type(goods_sn) is str:
        key = get_goods_price_key(goods_sn)
        conn = get_redis_connection()
        val = conn.get(key)
        if type(val) is bytes:
            price = int(val)
    return price


def update_goods_price(goods_sn, price):
    conn = get_redis_connection()
    key = get_goods_price_key(goods_sn)
    conn.set(key, price)
    Goods.objects.filter(sn=goods_sn).update(price=price)


# 总成交额
def get_order_trading_amount(order_sn):
    trading_amount = 0
    try:
        deal_order_logs = DealOrderLogs.objects.filter(order_sn=order_sn).all()
        for row in deal_order_logs:
            total_price = row.deal_amount * row.deal_price
            trading_amount += total_price
    except Exception:
        pass
    return trading_amount


# 返换冻结资金
def return_froze_funds(order, return_amount):
    err = None
    try:
        with transaction.atomic():
            # member_fund_logs = MemberFundLogs.objects.filter(
            #     subject_sn=order.order_sn,
            #     subject_type=SUBJECT_TYPE_BUY_DEAL,
            # ).get()
            member = Members.objects.filter(id=order.member_id).get()
            mem_ver = member.ver
            frozen_funds = member.frozen_funds
            amount_prev = member.amount
            remain_frozen = frozen_funds - return_amount
            amount_next = amount_prev + return_amount
            if remain_frozen >= 0 and amount_next >= 0:
                new_mem_data = {
                    'amount': amount_next,
                    'frozen_funds': remain_frozen,
                    'ver': mem_ver + 1
                }
                Members.objects.filter(id=order.member_id, ver=mem_ver).update(**new_mem_data)
                order_ver = order.ver
                new_order_data = {
                    'is_cancel': ORDER_STATUS_CANCEL,
                    'ver': order_ver + 1
                }
                Orders.objects.filter(id=order.id, ver=order_ver).update(**new_order_data)
                member_fund_logs = MemberFundLogs()
                member_fund_logs.sn = create_member_fund_log_sn()
                member_fund_logs.member_id = order.member_id
                member_fund_logs.subject_type = SUBJECT_TYPE_THAW_FUNDS
                member_fund_logs.subject_sn = order.order_sn
                member_fund_logs.amount = return_amount
                member_fund_logs.amount_prev = amount_prev
                member_fund_logs.amount_next = amount_next
                member_fund_logs.status = 1
                member_fund_logs.created = int(time.time())
                member_fund_logs.save()
    except Exception as ex:
        print(ex)
        err = "返还资金失败"
    return err


# 返换冻结资金
def return_froze_goods(order, return_amount):
    err = None
    try:
        with transaction.atomic():
            member_goods = MemberGoods.objects.filter(member_id=order.member_id, goods_sn=order.goods_sn).get()
            mem_goods_ver = member_goods.ver
            frozen_amount = member_goods.frozen_amount
            amount_prev = member_goods.amount
            remain_frozen = frozen_amount - return_amount
            amount_next = amount_prev + return_amount
            if remain_frozen >= 0 and amount_next >= 0:
                new_mem_goods_data = {
                    'amount': amount_next,
                    'frozen_amount': remain_frozen,
                    'ver': mem_goods_ver + 1
                }
                MemberGoods.objects.filter(id=member_goods.id, ver=mem_goods_ver).update(**new_mem_goods_data)
                order_ver = order.ver
                new_order_data = {
                    'is_cancel': ORDER_STATUS_CANCEL,
                    'ver': order_ver + 1
                }
                Orders.objects.filter(id=order.id, ver=order_ver).update(**new_order_data)
    except Exception:
        err = "返还资金失败"
    return err


def handle_cancel_order(sn):
    try:
        order = Orders.objects.filter(order_sn=sn).get()
        if order.status != 100:
            side = order.trade_type
            if side == 1:  # 买单，取消需要解冻资金
                order_sn = order.order_sn
                trading_amount = get_order_trading_amount(order_sn)
                remainder = order.total_price - trading_amount
                # 需要解冻的金额
                if remainder > 0:
                    err = return_froze_funds(order, remainder)
                    if err is not None:
                        raise Exception(err)
            elif side == 2:  # 卖单，取消需要解冻商品
                goods_remainder = order.amount - order.deal_amount
                if goods_remainder > 0:
                    err = return_froze_goods(order, goods_remainder)
                    if err is not None:
                        raise Exception(err)
            else:
                raise Exception("无效的交易类型")
    except Exception as ex:
        print('撤销订单失败', ex)
    return


def notify(request):
    form = RawJsonForm(request.body)
    event = form.get('event')
    if event == 'order.arrive':
        sn = form.get('sn')
        Orders.objects.filter(order_sn=sn).update(is_to_eng=100)
    elif event == 'order.cancel':
        sn = form.get('sn')
        handle_cancel_order(sn)
    return success()


def cancel_order(request):
    return_code, return_content = check_post_request(request)
    if return_code != 0:
        return return_content
    member_id = return_content

    form = RawJsonForm(request.body)
    order_sn = form.get('order_sn')

    order = Orders.objects.filter(order_sn=order_sn, member_id=member_id).get()
    if order.status == 100:
        return error('订单已成交')
    if order.is_cancel != 0:
        return error('订单已撤销')
    try:
        new_status = -1
        if order.status == 1:
            new_status = -2
        order_id = order.id
        ver = order.ver
        new_order_data = {
            'status': new_status,
            'is_cancel': 1,
            'ver': ver + 1,
            'updated': int(time.time())
        }
        Orders.objects.filter(id=order_id, ver=ver).update(**new_order_data)
        trade_data = {
            'order_sn': order_sn,
            'side': 3,
            'account_id': str(member_id),
        }
        result, err = push_to_trade_engine(trade_data)
        if err is not None:
            raise Exception('调用撮合引擎异常')
    except Exception as ex:
        print(ex.args)
        return error()

    return success()


def save_deal_order_log(uid, sn, price, amount, now):
    deal_order_log2 = DealOrderLogs()
    deal_order_log2.member_id = uid
    deal_order_log2.order_sn = sn
    deal_order_log2.deal_price = price
    deal_order_log2.deal_amount = amount
    deal_order_log2.created = now
    deal_order_log2.save()


async def notify_has_order(request):
    form = RawJsonForm(request.body)
    symbol = form.get('symbol')
    channel_layer = get_channel_layer()
    payload = {
        'type': 'market.orders',
        'data': {
            'symbol': symbol,
            'size': 5
        },
    }
    await channel_layer.group_send('market', payload)
    return success()
